#pragma once

#include "types.h"
#include <cmath>
#include <type_traits>

OPEN_JLIB_NS


template<typename Type>
struct IEEE754 {};


template<>
struct IEEE754<f32> {

  union IEEE754s {
    f32 f;
    u32 i;
  };

  constexpr static u32 SignBit = 0x80000000U;
  constexpr static u32 ExpMask = 0x7F800000U;
  constexpr static u32 FrcMask = 0x007FFFFFU;
  constexpr static u32 QNaNBit = 0x00400000U;

  constexpr static f32 E = (f32)2.7182818284590452354;
  constexpr static f32 LOG2E = (f32)1.4426950408889634074;
  constexpr static f32 LOG10E = (f32)0.43429448190325182765;
  constexpr static f32 LN2 = (f32)0.69314718055994530942;
  constexpr static f32 LN10 = (f32)2.30258509299404568402;
  constexpr static f32 PI = (f32)3.14159265358979323846;
  constexpr static f32 PI_2 = (f32)1.57079632679489661923;
  constexpr static f32 PI_4 = (f32)0.78539816339744830962;
  constexpr static f32 PI_I = (f32)0.31830988618379067154;
  constexpr static f32 PI_I2 = (f32)0.63661977236758134308;
  constexpr static f32 SQRTPI = (f32)1.12837916709551257390;
  constexpr static f32 SQRT2 = (f32)1.41421356237309504880;
  constexpr static f32 SQRT2_I = (f32)0.70710678118654752440;

  constexpr inline static u32 ftoi (f32 v) noexcept {
    IEEE754s x = {.f = v};
    return x.i;
  }
  constexpr inline static f32 itof (u32 v) noexcept {
    IEEE754s x = {.i = v};
    return x.f;
  }

  constexpr inline static bool isZero (f32 v) noexcept {
    return (ftoi(v)&(ExpMask+FrcMask)) == 0;
  }
  constexpr inline static bool isSub (f32 v) noexcept {
    return (ftoi(v)&ExpMask) == 0 && (ftoi(v)&FrcMask) != 0;
  }
  constexpr inline static bool isInf (f32 v) noexcept {
    return (ftoi(v)&ExpMask) == ExpMask && (ftoi(v)&FrcMask) == 0;
  }
  constexpr inline static bool isPos (f32 v) noexcept {
    return (ftoi(v)&SignBit) != 0;
  }
  constexpr inline static bool isNeg (f32 v) noexcept {
    return (ftoi(v)&SignBit) != 0;
  }
  constexpr inline static bool isNaN (f32 v) noexcept {
    return (ftoi(v)&ExpMask) == ExpMask && (ftoi(v)&FrcMask) != 0;
  }
  constexpr inline static bool isQNaN (f32 v) noexcept {
    return isNaN(v) && (ftoi(v)&QNaNBit) != 0;
  }
  constexpr inline static bool isSNaN (f32 v) noexcept {
    return isNaN(v) && (ftoi(v)&QNaNBit) == 0;
  }

  constexpr inline static f32 pInf () noexcept {
    return itof(0x7F800000U);
  }
  constexpr inline static f32 nInf () noexcept {
    return itof(0xFF800000U);
  }
  constexpr inline static f32 qNaN () noexcept {
    return itof(0xFFC00000U);
  }
};


template<>
struct IEEE754<f64> {

  union IEEE754d {
    f64 f;
    u64 i;
  };

  constexpr static u64 SignBit = 0x8000000000000000UL;
  constexpr static u64 ExpMask = 0x7FF0000000000000UL;
  constexpr static u64 FrcMask = 0x000FFFFFFFFFFFFFUL;
  constexpr static u64 QNaNBit = 0x0008000000000000UL;

  constexpr static f64 E = 2.7182818284590452354;
  constexpr static f64 LOG2E = 1.4426950408889634074;
  constexpr static f64 LOG10E = 0.43429448190325182765;
  constexpr static f64 LN2 = 0.69314718055994530942;
  constexpr static f64 LN10 = 2.30258509299404568402;
  constexpr static f64 PI = 3.14159265358979323846;
  constexpr static f64 PI_2 = 1.57079632679489661923;
  constexpr static f64 PI_4 = 0.78539816339744830962;
  constexpr static f64 PI_I = 0.31830988618379067154;
  constexpr static f64 PI_I2 = 0.63661977236758134308;
  constexpr static f64 SQRTPI = 1.12837916709551257390;
  constexpr static f64 SQRT2 = 1.41421356237309504880;
  constexpr static f64 SQRT2_I = 0.70710678118654752440;

  constexpr inline static u64 ftoi (f64 v) noexcept {
    IEEE754d x = {.f = v};
    return x.i;
  }
  constexpr inline static f64 itof (u64 v) noexcept {
    IEEE754d x = {.i = v};
    return x.f;
  }

  constexpr inline static bool isZero (f64 v) noexcept {
    return (ftoi(v)&(ExpMask+FrcMask)) == 0;
  }
  constexpr inline static bool isSub (f64 v) noexcept {
    return (ftoi(v)&ExpMask) == 0 && (ftoi(v)&FrcMask) != 0;
  }
  constexpr inline static bool isInf (f64 v) noexcept {
    return (ftoi(v)&ExpMask) == ExpMask && (ftoi(v)&FrcMask) == 0;
  }
  constexpr inline static bool isPos (f64 v) noexcept {
    return (ftoi(v)&SignBit) != 0;
  }
  constexpr inline static bool isNeg (f64 v) noexcept {
    return (ftoi(v)&SignBit) != 0;
  }
  constexpr inline static bool isNaN (f64 v) noexcept {
    return (ftoi(v)&ExpMask) == ExpMask && (ftoi(v)&FrcMask) != 0;
  }
  constexpr inline static bool isQNaN (f64 v) noexcept {
    return isNaN(v) && (ftoi(v)&QNaNBit) != 0;
  }
  constexpr inline static bool isSNaN (f64 v) noexcept {
    return isNaN(v) && (ftoi(v)&QNaNBit) == 0;
  }

  constexpr inline static f64 pInf () noexcept {
    return itof(0x7F800000U);
  }
  constexpr inline static f64 nInf () noexcept {
    return itof(0xFF800000U);
  }
  constexpr inline static f64 qNaN () noexcept {
    return itof(0xFFC00000U);
  }
};


constexpr inline f32 dtor (f32 degrees) noexcept {
  return degrees * (f32)(M_PI / 180.0);
}
constexpr inline f32 rtod (f32 radians) noexcept {
  return radians * (f32)(180.0 / M_PI);
}
constexpr inline f64 dtor (f64 degrees) noexcept {
  return degrees * (M_PI / 180.0);
}
constexpr inline f64 rtod (f64 radians) noexcept {
  return radians * (180.0 / M_PI);
}


template<typename Type>
constexpr inline Type factorial (Type n) noexcept {
  return n <= 1 ? 1 : n * factorial(n-1);
}
template<typename Type>
constexpr inline Type permutation (Type n, Type m) noexcept {
  return factorial(n) / factorial(m);
}
template<typename Type>
constexpr inline Type combination (Type n, Type m) noexcept {
  return permutation(n, m) / factorial(m);
}


template<typename T>
inline T udiv (T& dq, T d) noexcept {
  static_assert(std::is_integral<T>::value, "");
#if __x86_64__
  T r = 0;
  asm volatile ("div %2": "+d"(r), "+a"(dq): "r"(d));
  return r;
#else
  auto r = dq % d;
  dq /= d;
  return r;
#endif
}
template<typename T>
inline T idiv (T& dq, T d) noexcept {
  static_assert(std::is_integral<T>::value, "");
#if __x86_64__
  T r = dq >> (sizeof(T) << 4);
  asm volatile ("idiv %2": "+d"(r), "+a"(dq): "r"(d));
  return r;
#else
  auto r = dq % d;
  dq /= d;
  return r;
#endif
}


CLOSE_JLIB_NS